기계음·영어 발화 감지 로직 분석

apps/web — use-ai-audio-noise-guard.ts / use-ai-english-guard.ts / use-monitor-session.ts  ·  2026-05-20

1. 전체 감지 시스템 개요

PPI는 AI(핑퐁이)가 비정상 오디오를 출력할 때 이를 감지하는 두 개의 독립적인 방어 시스템을 운영합니다.

🔊 기계음 감지 Noise Guard

AI 오디오 스트림의 진폭 변동계수(CV)영교차율(ZCR)을 분석하여 기계적으로 균일한 파형을 감지합니다.

파일: hooks/use-ai-audio-noise-guard.ts

🗣️ 영어 발화 감지 MFCC Guard

AI 오디오 스트림에서 MFCC 특징을 추출해 사전 녹음된 영어 발음 템플릿과 코사인 유사도를 비교합니다.

파일: hooks/use-ai-english-guard.ts

2. 엔드-투-엔드 데이터 흐름

Guest (아동 브라우저)
AI 오디오 스트림 수신
OpenAI Realtime API → aiAudioStream (MediaStream)
기계음 감지 (병렬 실행)
useAiAudioNoiseGuard
50ms 주기 AnalyserNode 분석 · CV + ZCR 판정 · 2-stage 재검증
+
영어 발화 감지 (병렬 실행)
useAiEnglishGuard
ScriptProcessorNode → MFCC 추출 → 코사인 유사도 → 패턴 시퀀스 매칭
↓   감지 시
Guest Action (기계음)
cancelResponse() 호출 + sendIssueReport()
소켓 이벤트: GUEST_ISSUE_REPORT { issueText: "AI 오디오 기계음 감지 - 자동 중단", icon: "🔊" }
|
Guest Action (영어)
cancelResponse() 호출 + emitMfccEventToMonitor()
소켓 이벤트: GUEST_MFCC_LOG_EVENT { event: { type: "english_detected", sim, phrase, consecutive } }
↓   Socket.io 전달
Socket Server
GROUP_ALERT_BROADCAST 릴레이
같은 그룹 내 다른 세션 모니터들에게도 이벤트 브로드캐스트
Monitor (치료사 브라우저) — use-monitor-session.ts
handleGuestIssueReport / handleMfccLogEvent
이벤트 수신 → AlertType 매핑 → setAlerts + useMonitorDataStore.addAlert
↓   사이드이펙트
모니터 UI
Alert 패널 + Toast 알림
issueToast (3s 자동 닫힘) · alerts[] 배열 추가
+
MFCC 로그 (영어 감지 시)
turn_end 수신 → S3 업로드
JSONL blob → /api/session-logs/{turnId}/mfcc-upload-url → S3 PUT → PATCH mfccS3Key

3. 기계음 감지 알고리즘 상세

파일: apps/web/hooks/use-ai-audio-noise-guard.ts

3-1. 핵심 파라미터

상수설명
ANALYSIS_INTERVAL_MS50분석 주기 (ms)
RING_BUFFER_SIZE141차 판정 링 버퍼 크기 (14프레임 = 700ms)
NOISE_FRAME_THRESHOLD10링 버퍼 중 기계음 판정 필요 프레임 수 (≈71%)
CV_THRESHOLD0.35RMS 변동계수 임계값 (이하 → 기계음 의심)
ZCR_MIN_THRESHOLD0.04영교차율 최소값 (이 이상이어야 기계음 판정 유효)
RMS_SILENCE_FLOOR_DB–50 dB무음 필터 기준 (이하는 판정 제외)
GRACE_PERIOD_MS500AI 발화 시작 후 판정 유예 구간
REVERIFY_DURATION_MS2001차 감지 후 2차 재검증 기간
REVERIFY_FRAMES4재검증 프레임 수
REVERIFY_THRESHOLD3재검증 통과 필요 기계음 프레임 수
COOLDOWN_MS10,000연속 감지 억제 쿨다운 (ms)
RMS_WINDOW_SIZE8CV 계산용 RMS 슬라이딩 윈도우 크기 (400ms)
FFT_SIZE2048AnalyserNode FFT 크기

3-2. 판정 파이프라인

무음 필터
50ms 마다 AnalyserNode에서 Float32Array 읽음. RMS가 –50 dB 미만이면 해당 프레임 건너뜀.
RMS 슬라이딩 윈도우 (8프레임)
최근 8프레임(400ms)의 dB RMS를 linear 변환 후 배열에 유지. CV 계산에 사용.
CV 계산 (변동계수)
CV = std(linear_rms) / mean(linear_rms)
정상 음성 CV ≈ 0.85 · 기계음 CV ≈ 0.27. 임계값 0.35 이하이면 기계음 후보.
ZCR 보조 조건
영교차율이 0.04 이상이어야 판정 유효. 너무 매끈한 저주파 구간의 오탐을 보완.
1차 판정 — 링 버퍼
14프레임 원형 버퍼에 기계음 여부 Boolean 기록. 10/14 이상이면 후보 감지.
2차 재검증 (200ms / 4프레임)
후보 감지 후 200ms 추가 관찰. 4프레임 중 3프레임 이상 기계음이면 확정 감지. 아니면 링 버퍼 초기화 후 재시작.
쿨다운 체크 (10초)
마지막 감지로부터 10초 이내면 콜백 호출 억제.
onNoiseDetected() 콜백
cancelResponse() 호출 → AI 응답 즉시 중단.
guestSocket.sendIssueReport("AI 오디오 기계음 감지 - 자동 중단", "🔊") 전송.
설계 의도: 리뷰 반영으로 1차 감지 즉시 중단 대신 200ms 재검증을 추가. 짧은 무음 dip이나 발화 초기 과도기(500ms 유예)에서 발생하는 오탐을 줄이기 위한 구조.

4. 영어 발화 감지 알고리즘 상세 (MFCC)

파일: apps/web/hooks/use-ai-english-guard.ts + apps/web/lib/mfcc.ts

4-1. MFCC 추출 파라미터

파라미터설명
SAMPLE_RATE16,000 Hz처리 샘플레이트 (입력을 여기로 다운샘플)
FRAME_SIZE400 samples25ms @ 16kHz
HOP_SIZE160 samples10ms 스텝
FFT_SIZE512Radix-2 Cooley-Tukey FFT
NUM_MEL_FILTERS26Mel 필터뱅크 수
NUM_MFCC13MFCC 계수 수 (c0~c12)
PRE_EMPHASIS0.97고주파 강조 계수

4-2. 슬라이딩 윈도우 설정

상수설명
WINDOW_SAMPLES_16K4,800300ms @ 16kHz 분석 윈도우
HOP_SAMPLES_16K2,400150ms 슬라이딩 간격
SCRIPT_PROCESSOR_BUFFER_SIZE4,096ScriptProcessorNode 버퍼
MIN_RMS_ENERGY0.005무음 구간 스킵 기준
GRACE_PERIOD_MS500AI 발화 시작 후 유예 구간
COOLDOWN_MS10,000재감지 억제 쿨다운
TOP_N_TEMPLATES3검증 로그에 포함할 상위 유사도 템플릿 수
MAX_SILENCE_HOPS_FOR_RESET5연속 무음 hop 한계 (≈750ms 까지 커서 유지)
PATTERN_RULE_MISS_TOLERANCE2패턴 시퀀스 miss 허용 횟수

4-3. 감지 알고리즘 — 두 가지 모드

모드 A: 패턴 시퀀스 매칭 주 감지 방식
mfccPatternRules 존재 시 활성화
서버에서 내려온 MfccPatternRule[]로 순서 있는 템플릿 시퀀스 정의
각 rule에 cursor 유지
ruleCursorsRef: Map<ruleId, cursor> — 현재 몇 번째 스텝 대기 중인지 추적
스텝 매칭 조건
maxSimilarity ≥ 전역 threshold AND bestTemplateIdx === 기대 tldx AND sim ≥ 스텝별 threshold
miss 허용 (PATTERN_RULE_MISS_TOLERANCE = 2)
2회 이상 miss 시 cursor 리셋. 음절 경계 dip에서 시퀀스가 중단되지 않도록 보완.
시퀀스 완성 + stopSpeech=true
onEnglishDetected(tldxSequence) 호출 → AI 응답 즉시 중단
모드 B: Consecutive 매칭 로그 전용
패턴 규칙 없을 때 fallback
연속 매칭 카운터(consecutiveMatchCount)로 판단
같은 phraseGroup 내에서만 누적
그룹이 바뀌거나 offset이 감소하면 카운터 리셋
threshold 도달 시 로그만 남김
※ 현재는 action 없음 — 패턴 시퀀스 감지가 주 트리거

4-4. MFCC 추출 파이프라인

PCM 입력 (임의 SR)
16kHz 다운샘플 (선형 보간)
Pre-emphasis (α=0.97)
Hamming 윈도우 (25ms)
FFT-512 (Radix-2)
파워 스펙트럼
Mel 필터뱅크 26개
log(E)
DCT-II → 13 계수
c0 제거 후 코사인 유사도
c0(에너지 계수) 제거 이유: MFCC의 첫 번째 계수(c0)는 모든 음성에서 비슷한 에너지 레벨을 가져 오탐을 유발합니다. c1~c12만 비교해 음색 유사도를 더 정확하게 측정합니다.

5. 모니터 측 MFCC 로그 수집 시스템

파일: entities/monitor-session/model/use-monitor-session.ts + hooks/use-mfcc-log-collector.ts

5-1. 턴 버퍼 구조

버퍼 관련 상수
상수
MONITOR_MFCC_MAX_EVENTS_PER_TURN2,000
MFCC_TURN_END_FLUSH_GRACE_MS1,500 ms
MFCC_TURN_END_FLUSH_INFLIGHT_POLL_MS200 ms
MFCC_TURN_END_FLUSH_MAX_WAIT_MS6,000 ms
핵심 Ref 변수
변수설명
currentTurnBufferRef현재 턴 이벤트 버퍼 (MfccLogEvent[])
inActiveTurnRefai_speaking_start ~ turn_end 구간 flag
currentTurnUserIdRef현재 턴 소유 userId
pendingTurnFlushRefgrace 지연 flush 타이머 정보
mfccUploadInProgressRef중복 업로드 방지 Set<turnId>

5-2. 이벤트 타입 분류 (MfccLogEvent)

type설명소비 방식
session_start세션 시작 메타 정보버퍼 append
session_end세션 종료버퍼 append
ai_speaking_startAI 발화 시작 → 새 턴 시작이전 미완결 버퍼 flush + inActiveTurn=true
ai_speaking_stopAI 발화 중단버퍼 append
transcript발화 전사 스냅샷버퍼 append
mfcc_sample300ms 윈도우 MFCC + 코사인 유사도버퍼 append
match_candidate연속 매칭 후보 (로그용)버퍼 append
english_detected영어 발화 최종 감지버퍼 append + onMfccEnglishDetected 콜백
pattern_step패턴 시퀀스 진행 스텝버퍼 append
resetconsecutive 카운터 리셋 이유버퍼 append
turn_end턴 종료 → grace flush 스케줄grace 타이머 시작 후 S3 업로드

5-3. turn_end 처리 — Grace Flush 흐름

소켓 이벤트
turn_end 수신 (GUEST_MFCC_LOG_EVENT)
처리 1
이전 pendingFlush 즉시 실행
이전 턴이 grace 중이었다면 clearTimeout 후 즉시 flushTurnMfcc 호출
처리 2
Grace 타이머 시작 (1,500ms)
inActiveTurn 유지 — 그 사이 도착하는 이벤트도 현재 버퍼에 수용
↓   1,500ms 후 (또는 다음 ai_speaking_start 시 즉시)
Inflight 폴링
getInflightLidDetection() 체크
Whisper 추론이 in-flight면 200ms 간격으로 재폴링. 최대 6,000ms 하드 캡.
↓   inflight 완료 또는 하드 캡
S3 업로드
uploadTurnMfccLog
/api/session-logs/{turnId}/mfcc-upload-url → Presigned URL → S3 PUT (JSONL)
DB 연결
registerTurnMfccLog
/api/session-logs/{turnId}/mfcc PATCH — mfccS3Key, mfccRange 업데이트

5-4. mfccRange 요약 메타 (summarizeTurnMfcc)

필드설명
startMs / endMs턴 이벤트 타임스탬프 범위
eventCount총 이벤트 수
sampleCountmfcc_sample 이벤트 수
detectedEnglishenglish_detected 이벤트 존재 여부
detectedByMfccdetectedEnglish와 동일
maxSimmfcc_sample 또는 english_detected 중 최대 코사인 유사도
matchedPatternSequencepattern_step에서 완성된 tldx 시퀀스 배열

6. 모니터 수신 처리 — Alert 생성

6-1. 이벤트 → AlertType 매핑

🔊
GUEST_ISSUE_REPORT → issueText.includes("기계음 감지")
AlertType.AI_MECHANICAL_NOISE · 우선순위 P0
즉시 토스트
🗣️
GUEST_ISSUE_REPORT → issueText.includes("영어 발화 감지")
AlertType.AI_ENGLISH_DETECTED · 우선순위 P0
즉시 토스트
🧩
GUEST_ISSUE_REPORT → issueText.includes("영어 발화 패턴 감지")
AlertType.MFCC_PATTERN_MATCHING · 우선순위 P0
즉시 토스트
🆘
GUEST_ISSUE_REPORT → 기타 issueText
AlertType.CHILD_HELP_REQUEST · 우선순위 P0
즉시 토스트

6-2. MFCC english_detected 이벤트 처리 흐름

handleMfccLogEvent 수신
roomId 필터링 후 event.type 분기
event.type === "ai_speaking_start"
inActiveTurn = true. 이전 pendingFlush가 있으면 즉시 실행.
event.type === "english_detected"
onMfccEnglishDetectedRef.current({ sim, phrase, consecutive }) 콜백 호출. 버퍼에도 append.
event.type === "turn_end"
Grace 타이머 스케줄. inActiveTurn = false 는 flush 완료 후 처리.

6-3. 중복 감지 방지 메커니즘

mountedMonitorRoomIds

카드뷰에서 같은 그룹의 여러 세션이 동시 마운트될 때 GROUP_ALERT_BROADCAST 중복 처리를 방지하는 모듈-레벨 Set. 원본 roomId를 담당하는 카드가 이미 직접 처리하므로 브로드캐스트 경로에서 skip.

mfccUploadInProgressRef

Set<turnId>로 동일 turnId에 대한 동시 S3 업로드를 방지. flushTurnMfcc 진입 시 체크하고, finally에서 삭제.

7. 연결 해제 시 버퍼 정리

guest-disconnected / peer-left 수신 시: pendingTurnFlushRef 타이머를 clearTimeout하고 currentTurnBufferRef를 비웁니다. turn_end 없이 끊어진 미완결 턴은 S3에 업로드하지 않고 폐기합니다 (튜닝 가치가 낮고, 다음 세션 시작 시 버퍼 오염 방지 목적).
컴포넌트 unmount 시: pendingTurnFlushRef 타이머만 clearTimeout합니다. 남은 버퍼는 폐기되며 S3 업로드 호출을 생략합니다.

8. 시스템 요약

항목 기계음 감지 (NoiseGuard) 영어 발화 감지 (MFCC Guard)
감지 원리 RMS CV < 0.35 + ZCR 조건 MFCC 코사인 유사도 + 패턴 시퀀스
분석 주기 50ms (AnalyserNode) 150ms 슬라이딩 hop (ScriptProcessor)
감지 확인 방식 2-stage (링 버퍼 + 재검증 200ms) 패턴 시퀀스 완성 (miss 2회 허용)
AI 응답 중단 cancelResponse() 즉시 호출 onEnglishDetected → cancelResponse()
소켓 이벤트 GUEST_ISSUE_REPORT GUEST_MFCC_LOG_EVENT
모니터 AlertType AI_MECHANICAL_NOISE (P0) AI_ENGLISH_DETECTED / MFCC_PATTERN_MATCHING (P0)
로그 저장 없음 JSONL → S3 (turn_end grace flush)
쿨다운 10초 10초
발화 시작 유예 500ms 500ms

생성: 2026-05-20  ·  분석 대상: apps/web (develop 브랜치)  ·  주요 파일: use-ai-audio-noise-guard.ts, use-ai-english-guard.ts, use-monitor-session.ts, use-mfcc-log-collector.ts, lib/mfcc.ts